package com.ld.admin.service.system.dbbackup.queue;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang.StringUtils;

import com.ld.admin.model.system.dbbackup.DbBackUpStatic;
import com.ld.admin.model.system.dbbackup.DbBackupManageModel;
import com.ld.admin.model.system.dbbackup.DbBackupSetModel;
import com.ld.admin.model.system.dbbackup.DbbackLogModel;
import com.ld.admin.model.system.dbbackup.DbbackUpInfoModel;
import com.ld.shieldsb.common.composition.util.PKUtil;
import com.ld.shieldsb.common.core.model.Result;
import com.ld.shieldsb.common.core.util.ArithUtil;
import com.ld.shieldsb.common.core.util.DateUtil;
import com.ld.shieldsb.common.core.util.os.OSUtil;
import com.ld.shieldsb.common.core.util.properties.PropertiesUtil;
import com.ld.shieldsb.common.web.BasicService;
import com.ld.shieldsb.dao.model.QueryModel;
import com.ld.shieldsb.db.model.DBSetInfo;
import com.ld.shieldsb.db.model.DBTableModel;
import com.ld.shieldsb.db.service.DBTabelService;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class DBbackupThread implements Runnable {
    public static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(1, 1, 20000, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>(100000));
    public static DBbackUpRunningQueue<DbbackLogModel> DBBACKUP_RUNNING_QUEUE = new DBbackUpRunningQueue<>();

    public static final BasicService basicService = new BasicService();
    public PropertiesUtil config = new PropertiesUtil("dbbackup.properties"); // 配置文件
    private DBSetInfo dbSetInfo;

    private DbBackupSetModel backupSetModel;
    private String bakFilePath;

    private Long userId;
    private String userName;

    public DBbackupThread(DBSetInfo dBSetInfo, DbBackupSetModel backupSetModel, String bakFilePath, Long userId, String userName) {
        this.dbSetInfo = dBSetInfo;
        this.backupSetModel = backupSetModel;
        this.bakFilePath = bakFilePath;
        this.userId = userId;
        this.userName = userName;
    }

    public static void toInitialize() {
        EXECUTOR.allowCoreThreadTimeOut(true); // 该值为true,核心线程存活时间生效
    }

    @Override
    public void run() {
        Result result = DBTabelService.testCon(dbSetInfo.getDbUrl(), dbSetInfo.getDbUserName(), dbSetInfo.getDbPassword());
        if (!result.getSuccess()) {
            DbbackLogModel model = new DbbackLogModel();
            model.setBackstatus(-1); // 备份中
            model.setContent("备份失败，数据库无法链接");
            model.setPercent("0");
            DBBACKUP_RUNNING_QUEUE.put(PKUtil.nextId() + "", model);
            return;
        }

        DBTabelService service = DBTabelService.getInstance(dbSetInfo);
        List<String> commend = new ArrayList<String>();
        String osName = OSUtil.getOSName();
        int tablesNum = 0;
        if (osName.indexOf("win") != -1) {
            String mysqlRoot = "";
            if (config.getInt("database.location", 0) == 1) { // 数据库和系统在同一个服务器上
                String sql = "select @@basedir as basePath from dual";
                Map<String, Object> mapMysqlRoot = service.getDAO().getMap(sql, new Object[] {});
                mysqlRoot = mapMysqlRoot.get("basePath").toString(); // 获取mysql的安装mu'l
            } else {
                QueryModel queryModel = basicService.getNormalQueryModel();
                DbBackupManageModel backupManageModel = basicService.getOne(DbBackupManageModel.class, queryModel);
                if (backupManageModel != null && StringUtils.isNotEmpty(backupManageModel.getMysqlRootLocation())) {
                    mysqlRoot = backupManageModel.getMysqlRootLocation();
                } else {
                    mysqlRoot = config.getString("database.mysql.root.location", "F://phpStudy//MySQL//");
                }
            }
            commend.add(mysqlRoot + "bin" + File.separator + "mysqldump");
        } else {
            commend.add("mysqldump");
        }
        commend.add("-h");
        commend.add(dbSetInfo.getHost());
        if (!"3306".equals(dbSetInfo.getPort())) {
            commend.add("-P");
            commend.add(dbSetInfo.getPort());
        }

        if (backupSetModel.getDbHandler().equals(DbBackUpStatic.DBBACKUP.DBHANDLER_STRUCTURE)) {
            commend.add("-d");
        }

        if (backupSetModel.getDbHandler().equals(DbBackUpStatic.DBBACKUP.DBHANDLER_DATA)) {
            commend.add("-t");
        }

        commend.add("-u");
        commend.add(dbSetInfo.getDbUserName());
        commend.add("-p" + dbSetInfo.getDbPassword());
        commend.add(dbSetInfo.getDbName());

        if (backupSetModel.getBackupType().equals(DbBackUpStatic.DBBACKUP.BACKUPTYPE_DATABASE)) {
            String sql = "SELECT COUNT(*)  FROM information_schema.TABLES  WHERE table_schema =?";
            tablesNum = service.getDAO().getCountSql(sql, new Object[] { dbSetInfo.getDbName() }); // 获取需要备份表的数量
        } else if (backupSetModel.getBackupType().equals(DbBackUpStatic.DBBACKUP.BACKUPTYPE_TABLE)) {
            if (StringUtils.isEmpty(backupSetModel.getTables())) { // 如果备份表没填，默认备份所有的表
                String sql = "SELECT COUNT(*)  FROM information_schema.TABLES  WHERE table_schema =?";
                tablesNum = service.getDAO().getCountSql(sql, new Object[] { dbSetInfo.getDbName() }); // 获取需要备份表的数量
            } else {
                if (backupSetModel.getTablesArgs().equals(DbBackUpStatic.DBBACKUP.TABLESARGS_CONTAIN)) { // 只备份配置的几个表
                    String[] tableArr = backupSetModel.getTables().split(",");
                    tablesNum = tableArr.length;// 获取需要备份表的数量
                    for (int i = 0; i < tablesNum; i++) {
                        commend.add(tableArr[i]);
                    }

                } else {
                    String[] tableArr = backupSetModel.getTables().split(",");
                    String tableName = "";
                    List<DBTableModel> dbList = service.getTables(tableName);
                    List<String> stringA = Arrays.asList(tableArr);
                    List<String> tableList = new ArrayList<>();
                    dbList.forEach(model -> {
                        tableList.add(model.getTableName());
                    });
                    tableList.removeAll(stringA);// c
                    tableList.forEach(str -> {
                        commend.add(str);
                    });
                    tablesNum = tableList.size();
                }
            }
        }
        String[] strs = (String[]) commend.toArray(new String[] {});

        Process process = null;
        try {
            Runtime runtime = Runtime.getRuntime();
            process = runtime.exec(strs);
            InputStream in = process.getInputStream();
            InputStreamReader input = new InputStreamReader(in, "utf8");
            String inStr;
            StringBuffer sb = new StringBuffer("");
            String outStr;
            // 组合控制台输出信息字符串
            BufferedReader br = new BufferedReader(input);
            int pro = 0;
            String table = "";
            boolean backflag = false;
            String day = DateUtil.getNowDateTime("yyyy-MM-dd_[HH_mm]");
            String backFileName = "db_bak_" + dbSetInfo.getDbName() + "_" + day + ".sql";
            FileOutputStream fout = new FileOutputStream(bakFilePath + File.separator + backFileName);
            OutputStreamWriter writer = new OutputStreamWriter(fout, "utf8");
            while ((inStr = br.readLine()) != null) {
                if (backupSetModel.getDbHandler().equals(DbBackUpStatic.DBBACKUP.DBHANDLER_ALL)) {

                    if (inStr.contains("Table structure for table")) {
                        table = StringUtils.substringBetween(inStr, "Table structure for table `", "`");
                        DbbackLogModel model = new DbbackLogModel();
                        model.setBackstatus(0); // 备份中
                        model.setContent("开始备份表:" + table);
                        int percent = (int) (ArithUtil.div(pro, tablesNum, 2) * 100);
                        model.setPercent(percent + "");
                        model.setBacknum(pro + "/" + tablesNum);
                        DBBACKUP_RUNNING_QUEUE.put(PKUtil.nextId() + "", model);
                        // addOrUpdate(model, false);
                    }
                    if (inStr.contains("Dumping data for table")) {
                        table = StringUtils.substringBetween(inStr, "Dumping data for table `", "`");
                        pro++;
                        int percent = (int) (ArithUtil.div(pro, tablesNum, 2) * 100);
                        DbbackLogModel model = new DbbackLogModel();
                        if (percent >= 100) {
                            backflag = true;
                        }
                        model.setBackstatus(0); // 备份中
                        model.setContent("结束备份表:" + table);
                        model.setPercent(percent + "");
                        model.setBacknum(pro + "/" + tablesNum);
                        DBBACKUP_RUNNING_QUEUE.put(PKUtil.nextId() + "", model);
                    }

                } else if (backupSetModel.getDbHandler().equals(DbBackUpStatic.DBBACKUP.DBHANDLER_STRUCTURE)) {
                    if (inStr.contains("Table structure for table")) {
                        table = StringUtils.substringBetween(inStr, "Table structure for table `", "`");
                        pro++;
                        int percent = (int) (ArithUtil.div(pro, tablesNum, 2) * 100);
                        DbbackLogModel model = new DbbackLogModel();
                        if (percent >= 100) {
                            backflag = true;
                        }
                        model.setBackstatus(0); // 备份中
                        model.setContent("备份表:" + table);
                        model.setPercent(percent + "");
                        model.setBacknum(pro + "/" + tablesNum);
                        DBBACKUP_RUNNING_QUEUE.put(PKUtil.nextId() + "", model);
                    }
                } else if (backupSetModel.getDbHandler().equals(DbBackUpStatic.DBBACKUP.DBHANDLER_DATA)) {
                    if (inStr.contains("Dumping data for table `")) {
                        table = StringUtils.substringBetween(inStr, "Dumping data for table `", "`");
                        DbbackLogModel model = new DbbackLogModel();
                        model.setBackstatus(0); // 备份中
                        model.setContent("开始备份表:" + table);
                        int percent = (int) (ArithUtil.div(pro, tablesNum, 2) * 100);
                        model.setPercent(percent + "");
                        model.setBacknum(pro + "/" + tablesNum);
                        DBBACKUP_RUNNING_QUEUE.put(PKUtil.nextId() + "", model);
                        // addOrUpdate(model, false);
                    }
                    if (inStr.contains("ALTER TABLE `" + table + "` ENABLE KEYS")) {
                        table = StringUtils.substringBetween(inStr, "ALTER TABLE `", "` ENABLE KEYS");
                        pro++;
                        int percent = (int) (ArithUtil.div(pro, tablesNum, 2) * 100);
                        DbbackLogModel model = new DbbackLogModel();
                        if (percent >= 100) {
                            backflag = true;
                        }
                        model.setBackstatus(0); // 备份中
                        model.setContent("结束备份表:" + table);
                        model.setPercent(percent + "");
                        model.setBacknum(pro + "/" + tablesNum);
                        DBBACKUP_RUNNING_QUEUE.put(PKUtil.nextId() + "", model);
                    }
                }
                writer.write(inStr + "\r\n");
            }

            backflag = true;
            if (backflag) {
                DbbackLogModel model = new DbbackLogModel();
                model.setContent("备份完成！");
                model.setBackstatus(1); // 备份完成
                model.setBacknum(pro + "/" + tablesNum);
                model.setPercent("100");
                DBBACKUP_RUNNING_QUEUE.put(PKUtil.nextId() + "", model);
                // addOrUpdate(model, false);

                DbbackUpInfoModel infoModel = new DbbackUpInfoModel();
                infoModel.setBackupFileName(backFileName);
                infoModel.setBackupFilePath(bakFilePath + File.separator + backFileName);
                infoModel.setBackupType(backupSetModel.getBackupType());
                infoModel.setTablesArgs(backupSetModel.getTablesArgs());
                infoModel.setTables(backupSetModel.getTables());
                infoModel.setDbHandler(backupSetModel.getDbHandler());
                infoModel.setTablesNum(tablesNum);
                basicService.addOrUpdate(infoModel, false, userId, userName);

            } else {
                DbbackLogModel model = new DbbackLogModel();
                model.setBackstatus(1); // 备份完成
                model.setContent("备份失败！");
                model.setPercent("100");
                model.setBacknum(pro + "/" + tablesNum);
                DBBACKUP_RUNNING_QUEUE.put(PKUtil.nextId() + "", model);
                // addOrUpdate(model, false);
            }

            // outStr = sb.toString();

            writer.flush();
            writer.close();
            input.close();
            in.close();
            log.info("数据库备份已成功！");

        } catch (Exception e) {
            log.info("数据库备份失败，请查看异常");
            e.printStackTrace();
            DbbackLogModel model = new DbbackLogModel();
            model.setBackstatus(-1); // 备份中
            model.setContent("mysql备份配置错误，备份失败，请检查dbbackup.properties文件是否配置正确");
            model.setPercent("0");
            DBBACKUP_RUNNING_QUEUE.put(PKUtil.nextId() + "", model);
            return;
        }
    }

    public static DbbackLogModel getDbbackLogModel() {
        return DBBACKUP_RUNNING_QUEUE.get();
    }

    public static void shutdown() {
        EXECUTOR.shutdown();
    }

}
